/**
 * 
 */
package wong.board.client;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import wong.board.client.Board.App;
import wong.board.client.http.JSONRequest;
import wong.board.client.http.JSONRequest.Callback;

import com.google.gwt.json.client.JSONObject;
import com.google.gwt.user.client.Window;

/**
 * <p>
 * The class <code>BoardApp</code> is an implementation of the interface
 * {@link App}.
 * </p>
 * <p>
 * This class consists of all the presenters this application might show. The
 * presenters will be instantiated dynamically as needed. Their views will be
 * created via deferred binding.
 * </p>
 * <p>
 * On starting the application a {@link JSONRequest} is done to obtain the
 * content data from the {@link Board#CONTENT_URL}.
 * </p>
 * 
 * @author wong
 * 
 * @see #setup()
 * @see #start()
 */
public class BoardApp implements Board.App {
	private static Logger logger = Logger.getLogger(BoardApp.class.getName());
	//
	private JSONRequest<JSONObject> request;
	//
	private BoardContent boardContent;
	private List<String> contentUrls;

	/**
	 * gets the <code>body</code> panel and creates a {@link HeaderPresenter}
	 * and adds its view to the <code>body</code> panel. The content of the
	 * header presenter's view will be used as the header.
	 */
	@Override
	public void setup() {
		logger.info("Setting up the app...");
		boardContent = new BoardContent();
		boardContent.setup();
		logger.info("The app is set up.");
	}

	/**
	 * creates a {@link ContentCallback} and starts a {@link JSONRequest} to the
	 * {@link Board#CONTENT_URL} to obtain the content data.
	 * 
	 * @see #handleContentReceived(JSONObject)
	 */
	@Override
	public void start() {
		if (request == null) {
			logger.info("Starting the app...");
			logger.config("Setup window handling.");
			logger.config("Creating new request.");
			request = new JSONRequest<JSONObject>();
			//
			contentUrls = new ArrayList<String>();
			logger.config("Adding default content url: " + Board.CONTENT_URL);
			contentUrls.add(Board.CONTENT_URL);

			List<String> contentParameters = Window.Location.getParameterMap().get("content");
			if (contentParameters != null) {
				logger.config("Adding '" + contentParameters.size() + "' content url(s): " + Board.CONTENT_URL);
				for (String url : contentParameters) {
					if (url.isEmpty()) {
						logger.warning("url is empty");
					} else if (contentUrls.contains(url)) {
						logger.info("url already added - it will be moved to the end: '" + url + "'");
						contentUrls.remove(url);
						contentUrls.add(url);
					} else {
						contentUrls.add(url);
					}
				}
			}
			//
			logger.config("Loading next content...");
			loadNextContent();
			logger.info("App has started and is waiting for content.");
		} else {
			logger.warning("App already started. Call will be ignored.");
		}
	}

	@Override
	public void stop() {
		logger.info("Stopping...");
		if (contentUrls != null) {
			contentUrls.clear();
		}
		cancelRequest(false);
		logger.info("Stopped.");
	}

	@Override
	public void flush() {
		logger.info("Flushing...");
		if (contentUrls != null) {
			contentUrls.clear();
			contentUrls = null;
		}
		cancelRequest(true);
		if (boardContent != null) {
			boardContent.flush();
			boardContent = null;
		}
	}

	private void cancelRequest(boolean flush) {
		if (request != null) {
			logger.config("Request is pending. Cancelling the request...");
			request.cancel();
			logger.config("The pending request has been cancelled.");
			if (flush) {
				request = null;
			}
		}
	}

	protected void handleContentReceived(JSONObject newContent) {
		logger.config("Handle content received.");
		boardContent.applyContent(newContent);
		loadNextContent();
	}

	private void loadNextContent() {
		if (contentUrls != null && !contentUrls.isEmpty()) {
			String url = contentUrls.remove(0);
			if (url != null) {
				logger.config("Send Request to: '" + url + "'");
				request.get(url, new ContentCallback());
				logger.config("Request sent. Remaining content(s): " + contentUrls.size());
				return;
			}
		}
		//
		handleNoMoreContent();
	}

	private void handleNoMoreContent() {
		logger.config("No more content to load. Attaching views...");
		boardContent.attachViews();
		logger.info("The views have been attached. The app is ready to use. Enjoy!");
	}

	protected void handleCancel() {
		logger.info("Content call has been cancelled.");
	}

	/* */

	private class ContentCallback implements Callback<JSONObject> {

		@Override
		public void onReceive(JSONObject value) {
			handleContentReceived(value);
		}

		@Override
		public void onError(Throwable error) {
			Board.getErrorHandler().handleError("Handle content error.", error);
			loadNextContent();
		}

		@Override
		public void onCancel() {
			handleCancel();
		}

	}

}
